﻿using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Fleck;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace net.xBei.WebApi.Repository {
    /// <summary>
    /// WS服务
    /// </summary>
    public abstract class WebSocketService : BackgroundService {
        private readonly ILogger<WebSocketService> logger;
        readonly WebSocketServer webSocketServer;
        readonly ImmutableList<IWebSocketConnection> allSockets = ImmutableList<IWebSocketConnection>.Empty;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="location"></param>
        /// <param name="logger"></param>
        protected WebSocketService(string location, ILogger<WebSocketService> logger) {
            this.logger = logger;
            webSocketServer = new WebSocketServer(location) {
                RestartAfterListenError = true,
            };
        }
        /// <inheritdoc/>
        protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
            var socketReady = false;
            while (!socketReady && !stoppingToken.IsCancellationRequested) {
                logger.LogInformation("启动WS {Time}", DateTime.UtcNow);
                socketReady = await StartWebSocket();
            }
        }
        /// <inheritdoc/>
        public override Task StopAsync(CancellationToken cancellationToken) {
            webSocketServer.Dispose();
            allSockets.Clear();
            return base.StopAsync(cancellationToken);
        }

        /// <summary>
        /// 连接的客户端数量
        /// </summary>
        public int ClientCount => allSockets.Count;
        /// <summary>
        /// 发送消息给客户端
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendAsync(string message) {
            await Task.WhenAll(allSockets.Select(client => client.Send(message)));
        }
        /// <summary>
        /// 发送消息给客户端
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public async Task SendAsync(byte[] buffer) {
            await Task.WhenAll(allSockets.Select(client => client.Send(buffer)));
        }
        /// <summary>
        /// 发送消息给客户端
        /// </summary>
        /// <param name="client"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendAsync(IWebSocketConnection client, string message) {
            await client.Send(message);
        }
        /// <summary>
        /// 发送消息给客户端
        /// </summary>
        /// <param name="client"></param>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public async Task SendAsync(IWebSocketConnection client, byte[] buffer) {
            await client.Send(buffer);
        }
        /// <summary>
        /// 接收到客户端消息
        /// </summary>
        /// <param name="client">发送消息的客户端</param>
        /// <param name="message"></param>
        private void OnData(IWebSocketConnection client, string message)
            => OnData(WebSocketData.Create(client, message));
        /// <summary>
        /// 接收到客户端消息
        /// </summary>
        /// <param name="client">发送消息的客户端</param>
        /// <param name="buffer"></param>
        private void OnData(IWebSocketConnection client, byte[] buffer)
            => OnData(WebSocketData.Create(client, buffer));
        /// <summary>
        /// 接收到客户端消息
        /// </summary>
        /// <param name="data"></param>
        protected abstract void OnData(WebSocketData data);
        private async Task<bool> StartWebSocket(bool reset = false) {
            try {
                if (reset) {
                    webSocketServer.Dispose();
                    allSockets.Clear();
                }

                webSocketServer.RestartAfterListenError = true;
                webSocketServer.Start(socket => {
                    socket.OnPing = (bytes) => {
                        //_logger.Info("WSS Ping", socket.ConnectionInfo.Path, "Ping");
                        //Console.WriteLine($"WSS Ping {socket.ConnectionInfo.Path} Ping");
                    };

                    socket.OnPong = (bytes) => {
                        //_logger.Info("WSS Pong", socket.ConnectionInfo.Path, "Pong");
                        //Console.WriteLine($"WSS Pong {socket.ConnectionInfo.Path} Pong");
                    };

                    socket.OnError = (ex) => {
                        logger.LogError(ex, "WSS Error {path}", socket.ConnectionInfo.Path);
                    };

                    socket.OnOpen = () => {
                        logger.LogInformation("WSS open {path}", socket.ConnectionInfo.Path);
                        allSockets.Add(socket);
                    };
                    socket.OnClose = () => {
                        logger.LogInformation("WSS close {path}", socket.ConnectionInfo.Path);
                        allSockets.Remove(socket);
                    };
                    socket.OnMessage = message => {
                        logger.LogInformation("WSS {path} get {message}", socket.ConnectionInfo.Path, message);
                        OnData(socket, message);
                    };
                    socket.OnBinary = buffer => {
                        logger.LogInformation("WSS {path} get data length={length}", socket.ConnectionInfo.Path, buffer.Length);
                        OnData(socket, buffer);
                    };
                });
                return true;
            } catch (Exception ex) {
                if (logger == null) {
                    Console.WriteLine($"WSS ERROR {ex.Message} ERROR");
                    Console.WriteLine(ex.StackTrace);
                    if (ex.InnerException != null) {
                        Console.WriteLine(ex.InnerException);
                        Console.WriteLine(ex.InnerException.StackTrace);
                    }
                } else {
                    logger.LogError(ex, "WS ERROR");
                }
            }
            await Task.Delay(30 * 1000);
            return false;
        }
    }
    /// <summary>
    /// 接收到的数据
    /// </summary>
    public class WebSocketData {
        /// <summary>
        /// 数据类型：1-字符串，2-二进制
        /// </summary>
        public int Type { get; set; }
        /// <summary>
        /// 数据
        /// </summary>
        public string Message { get; set; } = default!;
        /// <summary>
        /// 数据
        /// </summary>
        public byte[] Data { get; set; } = Array.Empty<byte>();
        /// <summary>
        /// 接收时间
        /// </summary>
        public DateTime Time { get; } = DateTime.UtcNow;
        /// <summary>
        /// 客户端
        /// </summary>
        public IWebSocketConnection Client { get; }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="client"></param>
        internal WebSocketData(IWebSocketConnection client) {
            Client = client;
        }
        internal static WebSocketData Create(IWebSocketConnection client, string message)
            => new(client) { Message = message, Type = 1 };
        internal static WebSocketData Create(IWebSocketConnection client, byte[] buffer)
            => new(client) { Data = buffer, Type = 2 };
    }
}
